﻿using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using FlashCards.Dto;
using System.Xml.Serialization;
using System.IO;
using System.Collections.Generic;
using FlashCards.Service.Serialization;
using System.Data;
using FlashCards.Service.Extensions;
using FlashCards.NeuralNetworks.Utils;

namespace FlashCards.Service
{
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class SilverlightService : ISilverlightService
    {

        public string GetCategories()
        {
            using (var context = new Model.FlashCardsEntities())
            {
                var categories = context.Categories.ToList().Select(x => x.ToDto()).ToList();
                return DataSerializer.SerializeDto(categories, typeof(List<Category>));
            }
        }

        public string GetUserData(int userId)
        {
            using (var context = new FlashCards.Model.FlashCardsEntities())
            {
                var user = context.Users.FirstOrDefault(x => x.UserId == userId);
                if (user != null)
                {
                    var returnedUser = new User { ID = user.UserId, Name = user.Name, Surname = user.Surname };
                    return DataSerializer.SerializeDto(returnedUser, typeof(User));
                }
                else
                    return null;
            }
        }


        public string AddNewFlashcardSet(string serializedSet)
        {
            return AddNewFlashcardSet(serializedSet, null);
        }

        public string AddNewFlashcardSet(string serializedSet, int? groupId)
        {
            try
            {
                if (!string.IsNullOrEmpty(serializedSet))
                {
                    XmlSerializer ser = new XmlSerializer(typeof(FlashcardSet));
                    StringReader reader = new StringReader(serializedSet);
                    var set = ser.Deserialize(reader) as FlashcardSet;
                    if (set != null)
                    {
                        using (var context = new Model.FlashCardsEntities())
                        {
                            var entitySet = set.ToEntity();

                            context.FlashcardSets.AddObject(entitySet);

                            if (groupId.HasValue)
                            {
                                var group = context.Groups.FirstOrDefault(x => x.GroupId == groupId.Value);
                                group.FlashcardSets.Add(entitySet);
                            }

                            context.SaveChanges();

                            var types = context.FlashcardPartTypes.Select(x => new FlashcardPartType
                            {
                                Id = x.FlashcardPartTypeId,
                                Name = x.Name
                            }).ToList();

                            StringWriter writer = new StringWriter();
                            ser.Serialize(writer, entitySet.ToDto(types));

                            return writer.ToString();
                        }
                    }
                }
                return string.Empty;
            }
            catch (ConstraintException ex)
            {
                throw new FaultException<CustomFault>(new CustomFault(ex.Message), "Exception occured: ");
            }
            catch (Exception ex)
            {
                throw new FaultException<CustomFault>(new CustomFault(ex.Message), "Exception occured: ");
            }
        }


        public string GetFlashcardPartTypes()
        {
            using (var context = new FlashCards.Model.FlashCardsEntities())
            {
                var types = context.FlashcardPartTypes.Select(x => new FlashcardPartType
                {
                    Id = x.FlashcardPartTypeId,
                    Name = x.Name
                }).ToList();

                XmlSerializer ser = new XmlSerializer(typeof(List<FlashcardPartType>));
                StringWriter wr = new StringWriter();
                ser.Serialize(wr, types);

                return wr.ToString();
            }
        }


        public string GetFlashcardSet(int setId)
        {
            using (var context = new FlashCards.Model.FlashCardsEntities())
            {
                var entitySet = context.FlashcardSets.SingleOrDefault(x => x.FlashcardSetId == setId);
                var partTypes = context.FlashcardPartTypes.ToList().Select(x => x.ToDto()).ToList();

                var set = entitySet.ToDto(partTypes);
                XmlSerializer ser = new XmlSerializer(typeof(FlashcardSet));
                StringWriter wr = new StringWriter();
                ser.Serialize(wr, set);

                return wr.ToString();
            }
        }


        public void UpdateExistingSet(string serializedSet)
        {
            try
            {
                XmlSerializer ser = new XmlSerializer(typeof(FlashcardSet));
                StringReader reader = new StringReader(serializedSet);
                var set = ser.Deserialize(reader) as FlashcardSet;
                if (set != null)
                {
                    using (var context = new Model.FlashCardsEntities())
                    {
                        List<Model.Flashcard> flashcardsToRemove = null;

                        var entitySet = context.FlashcardSets.Include("Flashcards").SingleOrDefault(x => x.FlashcardSetId == set.Id);
                        entitySet.UpdateFromDto(set, ref flashcardsToRemove);

                        if (flashcardsToRemove != null && flashcardsToRemove.Count > 0)
                            foreach (var item in flashcardsToRemove)
                                context.Flashcards.DeleteObject(item);

                        context.SaveChanges();
                    }
                }
            }
            catch (Exception ex)
            {
                throw new FaultException<CustomFault>(new CustomFault(ex.Message), "Exception occured: ");
            }
        }

        public void SaveRepeat(string serializedRepeat)
        {
            try
            {
                var serializer = new XmlSerializer(typeof(Repeat));
                var reader = new StringReader(serializedRepeat);
                var repeat = serializer.Deserialize(reader) as Repeat;

                using (var context = new Model.FlashCardsEntities())
                {
                    var lastRepeat = context.Repeats
                        .Where(x => x.FlashcardSetId == repeat.FlashcardSetId)
                        .Where(x => x.UserId == repeat.UserId)
                        .OrderByDescending(x => x.RepeatDate)
                        .Take(1)
                        .FirstOrDefault();

                    if (lastRepeat != null)
                    {
                        foreach (var item in lastRepeat.RepeatItems)
                        {
                            if (repeat.Items.FirstOrDefault(x => x.FlashcardId == item.FlashcardId) == null)
                            {
                                repeat.Items.Add(new RepeatItem
                                {
                                    FlashcardId = item.FlashcardId,
                                    RepeatMark = item.RepeatMark,
                                    IsCopied = true
                                });
                            }
                        }
                    }

                    var entityRepeat = repeat.ToEntity();
                    context.Repeats.AddObject(entityRepeat);

                    //context.SaveChanges();

                    //zapisywanie i przetwarzanie neural network
                    var entityNetwork = context.NeuralNetworks1.FirstOrDefault(x => x.UserId == entityRepeat.UserId && x.FlashcardSetId == entityRepeat.FlashcardSetId);

                    //using (var writer2 = new StreamWriter("beforechange.txt"))
                    //{
                    //    foreach (var layer in entityNetwork.NeuronsLayers)
                    //    {
                    //        foreach (var neuron in layer.ai_Neurons)
                    //        {
                    //            foreach (var weight in neuron.NeuronWeights)
                    //            {
                    //                writer2.WriteLine(string.Format("{0}\t{1}\t{2}\t{3}", weight.WeightId, weight.WeightValue, weight.SourceNeuronGuid, weight.NeuronGuid));
                    //            }
                    //        }
                    //    }
                    //}

                    if (entityNetwork == null)
                    {
                        //nowa sieć
                        var networkManager = new NeuralNetworkManager(entityRepeat);

                        //przetrenowywanie na unknown
                        networkManager.TrainNetworkForUnknown(20, 0.1);

                        var network = networkManager.GetNetworkModel();

                        var resultSet = new Model.NeuralNetworkResultSet();
                        resultSet.Date = DateTime.Now;
                        resultSet.UserID = network.UserId.Value;
                        resultSet.FlashcardSetID = repeat.FlashcardSetId;

                        foreach (var item in networkManager.GetResult())
                        {
                            var result = new Model.NeuralNetworkResults()
                            {
                                Unknown = (decimal)item.Value[0],
                                Poor = (decimal)item.Value[1],
                                Average = (decimal)item.Value[2],
                                Good = (decimal)item.Value[3],
                                FlashcardID = item.Key,
                                UserMark = 0
                            };

                            resultSet.ai_NeuralNetworkResults.Add(result);
                        }

                        context.NeuralNetworkResultSets1.AddObject(resultSet);

                        networkManager.TrainNetwork(3, 0.1);

                        var newSet = new Model.NeuralNetworkResultSet();
                        newSet.Date = DateTime.Now;
                        newSet.UserID = network.UserId.Value;
                        newSet.FlashcardSetID = repeat.FlashcardSetId;

                        foreach (var item in networkManager.GetResult())
                        {
                            var result = new Model.NeuralNetworkResults()
                            {
                                Unknown = (decimal)item.Value[0],
                                Poor = (decimal)item.Value[1],
                                Average = (decimal)item.Value[2],
                                Good = (decimal)item.Value[3],
                                FlashcardID = item.Key,
                                UserMark = repeat.Items.FirstOrDefault(x => x.FlashcardId == item.Key).RepeatMark
                            };

                            newSet.ai_NeuralNetworkResults.Add(result);
                        }

                        context.NeuralNetworkResultSets1.AddObject(newSet);

                        network = networkManager.GetNetworkModel();

                        context.NeuralNetworks1.AddObject(network);
                    }
                    else
                    {
                        var networkManager = new NeuralNetworkManager(entityRepeat, entityNetwork.CreateNetwork());
                        networkManager.TrainNetwork(3, 0.1);
                        var newNetwork = networkManager.GetNetworkModel();
                        entityNetwork = newNetwork.UpdateNetwork(entityNetwork);

                        //using (var writer = new StreamWriter("afterchange.txt"))
                        //{
                        //    foreach (var layer in entityNetwork.NeuronsLayers)
                        //    {
                        //        foreach (var neuron in layer.ai_Neurons)
                        //        {
                        //            foreach (var weight in neuron.NeuronWeights)
                        //            {
                        //                writer.WriteLine(string.Format("{0}\t{1}\t{2}\t{3}", weight.WeightId, weight.WeightValue, weight.SourceNeuronGuid, weight.NeuronGuid));
                        //            }
                        //        }
                        //    }
                        //}

                        //zapisywanie wyniku sieci do bazy
                        var resultSet = new Model.NeuralNetworkResultSet()
                        {
                            Date = DateTime.Now,
                            User = entityRepeat.User,
                            FlashcardSetID = entityRepeat.FlashcardSetId
                        };

                        foreach (var item in networkManager.GetResult())
                        {
                            var result = new Model.NeuralNetworkResults()
                            {
                                Unknown = (decimal)item.Value[0],
                                Poor = (decimal)item.Value[1],
                                Average = (decimal)item.Value[2],
                                Good = (decimal)item.Value[3],
                                FlashcardID = item.Key,
                                UserMark = repeat.Items.FirstOrDefault(x => x.FlashcardId == item.Key).RepeatMark
                            };

                            

                            resultSet.ai_NeuralNetworkResults.Add(result);
                        }

                        context.NeuralNetworkResultSets1.AddObject(resultSet);
                        
                    }

                    context.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                throw new FaultException<CustomFault>(new CustomFault(ex.Message), "Exception occured: ");
            }
        }

        public string GetUserLastRepeat(int userId, int flashcardSetId)
        {
            try
            {
                using (var context = new Model.FlashCardsEntities())
                {

                    //TODO: zoptymalizować to zapytanie

                    var repeat = context.Repeats
                        .Where(x => x.UserId == userId)
                        .Where(x => x.FlashcardSetId == flashcardSetId)
                        .OrderByDescending(x => x.RepeatDate)
                        .Take(1).FirstOrDefault();

                    var set = context.FlashcardSets.FirstOrDefault(x => x.FlashcardSetId == flashcardSetId);
                    if (repeat == null)
                    {
                        repeat = new Model.Repeat();
                        foreach (var item in set.Flashcards)
                        {
                            repeat.RepeatItems.Add(new Model.RepeatItem
                            {
                                FlashcardId = item.FlashcardId,
                                RepeatMark = 0
                            });
                        }
                    }
                    else
                    {
                        foreach (var item in set.Flashcards)
                        {
                            if (repeat.RepeatItems.FirstOrDefault(x => x.FlashcardId == item.FlashcardId) == null)
                            {
                                repeat.RepeatItems.Add(new Model.RepeatItem
                                {
                                    FlashcardId = item.FlashcardId,
                                    RepeatMark = 0
                                });
                            }
                        }
                    }

                    var repeatItems = repeat.RepeatItems.Select(x => x.ToDto()).ToList();

                    var serializer = new XmlSerializer(typeof(List<RepeatItem>));
                    using (var writer = new StringWriter())
                    {
                        serializer.Serialize(writer, repeatItems);
                        return writer.ToString();
                    }
                }
            }
            catch (Exception ex)
            {
                throw new FaultException<CustomFault>(new CustomFault(ex.Message), "Exception occured: ");
            }
        }

        public string GetNetworkLastResult(int userId, int flashcardSetId)
        {
            using (var context = new Model.FlashCardsEntities())
            {
                var resultSet = context.NeuralNetworkResultSets1
                    .Where(x => x.UserID == userId)
                    .Where(x => x.FlashcardSetID == flashcardSetId)
                    .OrderByDescending(x => x.Date)
                    .Take(1)
                    .FirstOrDefault();

                var list = new List<NetworkResult>();

                foreach (var item in resultSet.ai_NeuralNetworkResults)
                {
                    list.Add(item.ToDto());
                }

                var serializedList = DataSerializer.SerializeDto(list, typeof(List<NetworkResult>));
                return serializedList;
            }
        }
    }
}
